package compilador;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Vector;

import maquina.Dato;

import repertorioInstrucciones.Instruccion;


import parser.Parser;

import analizadorLexico.estados.Contexto;

import analizadorLexico.tokens.Token;
import analizadorLexico.tokens.TokenError;

public class Compilar{

	//private static String nombreFichero;
	private static File fichero;
	private static File ficheroSalida;
	private static Parser parser;
	
	
	
	private static boolean comprobarParametrosYLeerFichero(String[] args)
	{
		/*compruebo si el numero de parametros es correcto*/
		if(args.length!=2)return false;
		
		fichero=new File(args[0]);
		ficheroSalida=new File(args[1]);
		
		return true;
	}
	
	
	
	
	
	public static void main(String[] args)
	{
		if(comprobarParametrosYLeerFichero(args))
		{
			try
			{
				System.out.println("Compilando... " + args[0]);
				long tiempoInicio= System.nanoTime();
				compila(new BufferedReader(new FileReader(fichero)));
				/*aqui se llamara al sintactico cuando este sacando informacion de la clase Contexto*/
				long tiempoFin = System.nanoTime();
				System.out.println("Compilado en "+(tiempoFin-tiempoInicio)+" ns");
			}
			catch(FileNotFoundException e)
			{
				System.err.println("La ruta del fichero \""+ fichero.getPath()+"\" no es correcta");
			}
		}
		else
		{
			System.out.println("El programa debe ejecutarse del siguiente modo:");
			System.out.println("java -jar <fichero fuente> <fichero destino>");
		}
	}
	
	
	
	
	
	@SuppressWarnings("unchecked")
	public static void compila(BufferedReader buffer)
	{
		
		
		String linea;		
		Contexto.getContexto();
		
		System.out.println("\n ///////////ANALISIS LEXICO\\\\\\\\\\\\\\ \n ");
		
		try 
		{ 
			while ((linea = buffer.readLine()) != null) 
				Contexto.appendToBuffer(linea+new Character('\n').toString()); 
		} 
		catch (IOException e) 
		{	
			System.err.println("Ha habido un error en la lectura del fichero");	
		}
		
		try 
		{ 
			Contexto.iniciarAutomata();			
		} 
		catch (Exception e) {System.err.println(e);}
		
		Vector<TokenError> listaErrores = Contexto.getListaErrores();
		Vector<Token> listaTokens = Contexto.getTokens();
		System.out.println("Errores encontrados:");
		for (TokenError error : listaErrores)
			System.err.println(error);
		System.out.println("Tokens encontrados:");
		for (Token token : listaTokens)
			System.out.println(token);
		
		//aqui ya se tiene la lista de Tokens generada
		//comenzamos con el analisis sintactico
		
		
		
		
		System.out.println ("\n ///////////ANALISIS SINTACTICO\\\\\\\\\\\\\\ \n ");
		
		boolean error=true;
		try{

			parser=new Parser();
			 error= parser.parsea();
			 if (error){
				 System.out.println ("\n NO SE HA GENERADO POR CONTENER ERRORES");
			 }
			 else if (!error)
			{//una vez parseado, obtenemos el codigo y lo guardamos
			
				// guardamos el arraylist de instrucciones en el fichero dado por parametro
				try {
					FileOutputStream stream = new FileOutputStream(ficheroSalida);
					ObjectOutputStream bf = new ObjectOutputStream(stream);
					parser.salvaCodigo(bf);
				} catch(FileNotFoundException e)
				{
					System.err.println("La ruta del fichero \""+ ficheroSalida.getPath()+"\" no es correcta");
				} 
			
			
				//mostramos por pantalla el codigo para mejor depuracion
				System.out.println ("\n CODIGO GENERADO Y QUE SE HA GUARDADO EN EL FICHERO:\n");
				ArrayList<Instruccion> a=parser.getCod();
				for(int i=0;i<a.size();i++)
				{
					Instruccion ins=a.get(i);
					String pintar=ins.toString();
					int j=0;
					while(j<pintar.length()&&pintar.charAt(j)!='.')
					{
						j++;
					}
					j++;
					pintar=pintar.substring(j,pintar.length()-1);
					
					System.out.println(pintar);
				}
			}
			
				
			
		}
		catch(Exception e) {
			System.err.println(e);
			
		}
		
		//////////////////////////////////////////////////////////////////
		
		if (!error){
			ObjectInputStream ob;
			try {
				ob = new ObjectInputStream(new FileInputStream(ficheroSalida));
				ArrayList<Instruccion> codigo = (ArrayList<Instruccion>) ob.readObject();
				ArrayList<Dato> memoria= new ArrayList<Dato> ();
				memoria=(ArrayList<Dato>) ob.readObject();
				//System.out.println ("\n RECUPERAMOS EL CODIGO A PARTIR DEL FICHERO:\n" + codigo.toString());
				System.out.println ("\n MEMORIA GENERADA:\n");
				String pintar=null;
				String[] pintar2=null;
				for(int i=0;i<memoria.size();i++)
				{
					Dato d=memoria.get(i);
					pintar=d.toString();
					int j=pintar.length()-1;
					while(pintar.charAt(j)!='.'&&j>=0)
					{
						j--;
					}
					pintar=pintar.substring(j+1);
					System.out.print("Posicion "+i+": ");
					if(pintar.equals("Integer"))
					{
						System.out.println("Entero");
					}
					else
					{
						if(pintar.equals("Float"))
						{
							System.out.println("Real");
						}
						else
						{
							if(pintar.equals("Boolean"))
							{
								System.out.println("Booleano");
							}
							else
							{
								if(pintar.equals("String"))
								{
									System.out.println("Caracter");
								}
								else
								{
									System.out.println("Natural");
								}
								
							}
						}
					}
					
					System.out.println();
				}
			
			}
		catch (Exception e){System.out.println(e.toString());}
		}
		//aqui ya se ha generado el fichero con el codigo, que contiene un arraylist de objetos de tipo
		//instruccion, cada uno de ellos con una instruccion de pila y su argumento
		//ya se podria ejecutar otro programa que leyese este fichero , sacase el arraylist y con el objetos 
		// de tipo instruccion , y de ellos obtener el tipo de operacion y argumento y comenzar
		//con las operaciones de pila
		
	}
}
